/* 
 * Copyright Nate Dierk 2013
 *
 * GBlitter Class for Comp590
 *
 */

#include "GTypes.h"
#include "GContext.h"
#include "GPaint.h"
#include "GRect.h"
#include "GBitmap.h"

#include "GMatrix_nd.h"
#include "GPixel_nd.cpp"

class GBlitter{
protected:
	GBitmap* dst_bmp;
 	GMatrix* CTM;

public:
	GBlitter(){}
	virtual ~GBlitter(){}
	virtual void blitRow(int x, int y, int count) = 0;

	static inline void memset_32(uint32_t* array, uint32_t value, int n){
		for (int i = 0; i < n; ++i){
			array[i] = value;
		}
	}

	GPixel* getPixelPointer(GBitmap* bmp, int x, int y){
		return (GPixel*) (((char*) (bmp->fPixels + x)) + // compute pointer to top left
                  (bmp->fRowBytes * y)); // add vertical offset
	}
};

// single color, SRC color (no blending), all pixels (no transform)
class ClearBlitter : public GBlitter {
protected:
	GPixel src_pixel;

public:
	ClearBlitter(){}
	ClearBlitter(GBitmap* _bmp, const GColor& color){
		dst_bmp = _bmp;
		src_pixel = GPixel_PackColor(color);
	}
	~ClearBlitter(){}

	void blitRow(int x, int y, int count){
      GPixel* dst = getPixelPointer(dst_bmp, x, y);
      memset_32(dst, src_pixel, count);
	};
};

// single color, SRC_OVER blending if alpha != 1.0f (if so, use clearBlitter!)
class RectBlitter : public ClearBlitter {
protected:
	unsigned alpha_inv;

public:
	RectBlitter(){}
	RectBlitter(GBitmap* bmp, const GPaint& paint){
		dst_bmp = bmp;
		src_pixel = GPixel_PackColor(paint.getColor());
		alpha_inv = GPIXEL_SCALE_FACTOR - GPixel_GetA(src_pixel);
	}
	~RectBlitter(){}

	void blitRow(int x, int y, int count){

		GPixel* dst = getPixelPointer(dst_bmp, x, y);

		for (int i = 0; i < count; ++i){
			GPixel_Scale(dst, alpha_inv);
			*dst += src_pixel;
			++dst;
		}
	}
};


class BmpBlitter : public RectBlitter {
protected:
	GBitmap src_bmp;
	unsigned alpha;

	bool sampleIsValid(int x, int y){
		return (x >= 0 && x < src_bmp.fWidth) && (y >= 0 && y < src_bmp.fHeight);
	}

	GPixel* xformAndSample(int x, int y){
        // transform point (x,y)
		GVector transformed = CTM->transform(GVector(x + 0.5f, y + 0.5f));
		int u = (int) floorf(transformed.fX);
		int v = (int) floorf(transformed.fY);

		if (sampleIsValid(u, v)){
			return getPixelPointer(&src_bmp, u, v);
		} else {
			return NULL;
		}
	}

public:
	BmpBlitter(){}
	BmpBlitter(GBitmap* bmp, const GBitmap& src, GMatrix* ctm, float a){
		dst_bmp = bmp;
		src_bmp = src;
		CTM = ctm;
		alpha = floorf((GPIXEL_SCALE_FACTOR * a) + 0.5f);
	}

	void blitRow(int x, int y, int count){
		GPixel* dst = getPixelPointer(dst_bmp, x, y);

		for (int i = 0; i < count; ++i){
			GPixel* src_pixel = xformAndSample(x+i, y);
		
			if (src_pixel){
				GPixel s_copy = *src_pixel;
				GPixel_Scale(&s_copy, alpha); // scale source pixel down by alpha param
				
				unsigned blend_a = GPIXEL_SCALE_FACTOR - GPixel_GetA(s_copy);
				GPixel_Scale(dst, blend_a); // scale down dest pixel by blend alpha_inv

				*dst += s_copy;
			}
			++dst;
		}
	}
};
